package com.ssy.lingxi.order.serviceimpl.base;

import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.order.entity.OrderTradeProcessDO;
import com.ssy.lingxi.order.entity.OrderTradeProcessPaymentDO;
import com.ssy.lingxi.order.model.bo.PayNodeBO;
import com.ssy.lingxi.order.model.vo.process.request.OrderTradeProcessPaymentGroupVO;
import com.ssy.lingxi.order.model.vo.process.request.OrderTradeProcessPaymentVO;
import com.ssy.lingxi.order.model.vo.process.response.OrderTradeProcessPaymentDetailGroupVO;
import com.ssy.lingxi.order.model.vo.process.response.OrderTradeProcessPaymentDetailVO;
import com.ssy.lingxi.order.repository.OrderTradeProcessPaymentRepository;
import com.ssy.lingxi.order.service.base.IBaseOrderTradeProcessPaymentService;
import com.ssy.lingxi.order.utils.NumberUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 交易流程关联的支付配置相关接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2021-07-25
 */
@Service
public class BaseOrderTradeProcessPaymentServiceImpl implements IBaseOrderTradeProcessPaymentService {
    @Resource
    private OrderTradeProcessPaymentRepository orderTradeProcessPaymentRepository;

    /**
     * 校验支付环节配置
     *
     * @param payTimes     基础流程设置的支付次数
     * @param payNodes     基础流程设置的支付环节
     * @param paymentList  接口参数
     * @return 校验结果
     */
    @Override
    public Wrapper<Void> checkPayments(Integer payTimes, List<PayNodeBO> payNodes, List<OrderTradeProcessPaymentGroupVO> paymentList) {
        if(NumberUtil.isNullOrZero(payTimes)) {
            return Wrapper.success();
        }

        // 支付配置不能为空
        if(CollectionUtils.isEmpty(paymentList)) {
            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_PAYMENT_SETTING_REQUIRED);
        }

        // 校验支付设置的条数，必须要与流程中的支付次数相等，并且batchNo必须相同
        if(paymentList.size() != payNodes.stream().max(Comparator.comparingInt(PayNodeBO::getSerialNo)).map(PayNodeBO::getSerialNo).orElse(0) && paymentList.stream().mapToLong(p -> p.getNodes().size()).sum() != payTimes || paymentList.stream().flatMap(p -> p.getNodes().stream()).anyMatch(payment -> payNodes.stream().noneMatch(payNode -> payNode.getBatchNo().equals(payment.getBatchNo())))) {
            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_PAYMENT_SETTING_TIMES_MUST_EQUAL_TO_PROCESS_PAY_TIMES);
        }

        // 校验支付次数（支付次数必须从1开始，每次递增1）
        if(!NumberUtil.integerListEquals(paymentList.stream().flatMap(p -> p.getNodes().stream()).map(OrderTradeProcessPaymentVO::getBatchNo).collect(Collectors.toList()), Stream.iterate(1, item -> item+1).limit(payTimes).collect(Collectors.toList()))) {
            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_PAYMENT_BATCH_MUST_START_WITH_ONE_INCREMENT_ONE);
        }

        // 支付比例相加要等于100
        if(paymentList.stream().flatMap(p -> p.getNodes().stream()).map(OrderTradeProcessPaymentVO::getPayRate).reduce(BigDecimal::add).orElse(BigDecimal.ZERO).compareTo(new BigDecimal(100)) != 0) {
            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_PAYMENT_RATE_SUM_MUST_EQUAL_TO_100);
        }

        return Wrapper.success();
    }

    /**
     * 保存支付环节配置，调用方要保存OrderTradeProcessDO
     *
     * @param tradeProcess 交易流程规则配置
     * @param payTimes     基础流程设置的支付次数
     * @param payNodes     基础流程设置的支付环节
     * @param paymentList  接口参数
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void savePayments(OrderTradeProcessDO tradeProcess, Integer payTimes, List<PayNodeBO> payNodes, List<OrderTradeProcessPaymentGroupVO> paymentList) {
        if(NumberUtil.isNullOrZero(payTimes)) {
            tradeProcess.setPayments(new HashSet<>());
            return;
        }

        // 保存
        List<OrderTradeProcessPaymentDO> payments = paymentList.stream().flatMap(p -> p.getNodes().stream()).map(p -> {
            OrderTradeProcessPaymentDO payment = new OrderTradeProcessPaymentDO();
            payment.setSerialNo(payNodes.stream().filter(payNode -> payNode.getBatchNo().equals(p.getBatchNo())).map(PayNodeBO::getSerialNo).findFirst().orElse(0));
            payment.setBatchNo(p.getBatchNo());
            payment.setPayNode(p.getPayNode());
            payment.setPayRate(p.getPayRate().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
            payment.setProcess(tradeProcess);
            return payment;
        }).collect(Collectors.toList());
        orderTradeProcessPaymentRepository.saveAll(payments);

        //设置交易流程的支付配置，在外需要保存OrderTradeProcessDO
        tradeProcess.setPayments(new HashSet<>(payments));
    }

    /**
     * 更新支付环节配置，调用方要保存OrderTradeProcessDO
     *
     * @param tradeProcess 交易流程规则配置
     * @param payTimes     基础流程设置的支付次数
     * @param payNodes     基础流程设置的支付环节
     * @param paymentList  接口参数
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void updatePayments(OrderTradeProcessDO tradeProcess, Integer payTimes, List<PayNodeBO> payNodes, List<OrderTradeProcessPaymentGroupVO> paymentList) {
        if(NumberUtil.isNullOrZero(payTimes)) {
            orderTradeProcessPaymentRepository.deleteByProcess(tradeProcess);
            tradeProcess.setPayments(new HashSet<>());
            return;
        }

        // 先删除，再保存
        orderTradeProcessPaymentRepository.deleteByProcess(tradeProcess);

        List<OrderTradeProcessPaymentDO> payments = paymentList.stream().flatMap(p -> p.getNodes().stream()).map(p -> {
            OrderTradeProcessPaymentDO payment = new OrderTradeProcessPaymentDO();
            payment.setSerialNo(payNodes.stream().filter(payNode -> payNode.getBatchNo().equals(p.getBatchNo())).map(PayNodeBO::getSerialNo).findFirst().orElse(0));
            payment.setBatchNo(p.getBatchNo());
            payment.setPayNode(p.getPayNode());
            payment.setPayRate(p.getPayRate().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
            payment.setProcess(tradeProcess);
            return payment;
        }).collect(Collectors.toList());
        orderTradeProcessPaymentRepository.saveAll(payments);

        //设置交易流程的支付配置，在外需要保存OrderTradeProcessDO
        tradeProcess.setPayments(new HashSet<>(payments));
    }

    /**
     * 查询支付环节配置列表
     *
     * @param tradeProcess 交易流程规则配置
     * @return 支付环节配置列表
     */
    @Override
    public List<OrderTradeProcessPaymentDetailGroupVO> findPayments(OrderTradeProcessDO tradeProcess) {
        List<OrderTradeProcessPaymentDO> payments = orderTradeProcessPaymentRepository.findByProcess(tradeProcess);
        return CollectionUtils.isEmpty(payments) ? new ArrayList<>() : payments.stream().collect(Collectors.groupingBy(OrderTradeProcessPaymentDO::getSerialNo)).entrySet().stream().map(entry -> {
            OrderTradeProcessPaymentDetailGroupVO groupVO = new OrderTradeProcessPaymentDetailGroupVO();
            groupVO.setSerialNo(entry.getKey());
            groupVO.setNodes(entry.getValue().stream().map(node -> {
                OrderTradeProcessPaymentDetailVO detailVO = new OrderTradeProcessPaymentDetailVO();
                detailVO.setBatchNo(node.getBatchNo());
                detailVO.setPayNode(node.getPayNode());
                detailVO.setPayRate(NumberUtil.formatAmount(node.getPayRate().multiply(BigDecimal.valueOf(100))));
                return detailVO;
            }).sorted(Comparator.comparingInt(OrderTradeProcessPaymentDetailVO::getBatchNo)).collect(Collectors.toList()));
            return groupVO;
        }).sorted(Comparator.comparingInt(OrderTradeProcessPaymentDetailGroupVO::getSerialNo)).collect(Collectors.toList());
    }
}
